#include "ukccsessionserver.h"

#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QDebug>

const QStringList mavisFilterPathList = {"/Date/Set Time","/Date/24-hour clock",
                                          "/Date/Other Timezone","/Date/Set Date Manually",
                                          "/Date/Change time zone","/Date/Sync Time",
                                          "/Date/Manual Time","/Date/Sync Server",
                                          "/Date/Auto Sync Time","/Date/Network",
                                          "/Mouse/Wheel speed","/Mouse/Double-click interval time",
                                          "/Mouse/Mouse acceleration", "/Theme/Cursor theme", "/Display/resolution",
                                          "/Display/orientation", "/Display/frequency", "/Display/screen zoom", "/Theme/Transparency",
                                          "/Audio/Sound Theme"
                                         };
const QStringList filterPathList = { "/Display/Auto Brightness", "/Display/Dynamic light" };

const QStringList openkylinFilterPathList = {"/Shortcut/Add", "/Shortcut/Customize Shortcut"};

ukccSessionServer::ukccSessionServer() {
   mFilePath = QDir::homePath() + "/.config/ukui/ukcc-screenPreCfg.json";
   monitoFileChanged();
}

QMap<QString, QVariant> ukccSessionServer::getJsonInfo(const QString &configFile) {
    QVariantMap moduleMap;

    QFile file(configFile);
    if (file.exists()) {
        file.open(QIODevice::ReadOnly);
        QByteArray readBy=file.readAll();
        QJsonParseError error;
        QJsonDocument readDoc=QJsonDocument::fromJson(readBy,&error);
        QJsonArray obj=readDoc.object().value("ukcc").toArray();

        for (int i = 0 ; i < obj.size(); i++) {
            QJsonObject faObj= obj[i].toObject();
            moduleMap.insert(faObj["name"].toString(), faObj["visible"].toVariant());
            QJsonArray childNodeAry =  faObj["childnode"].toArray();
            for (int j = 0; j < childNodeAry.size(); j++) {
                QString modeName = childNodeAry.at(j).toObject().value("name").toString();
                QString modeSet = modeName + "Settings";
                QVariant modeVisiable = childNodeAry.at(j).toObject().value("visible").toVariant();

                moduleMap.insert(modeName, modeVisiable);
                moduleMap.insert(modeSet, childNodeAry.at(j).toObject().value(modeSet).toString());
            }
        }
    }
    return moduleMap;
}

QString ukccSessionServer::getScreenMode()
{
    return mScreenMode;
}

void ukccSessionServer::setScreenMode(QString screenMode)
{
    mScreenMode = screenMode;
    Q_EMIT screenChanged(mScreenMode);
}

void ukccSessionServer::setPreScreenCfg(QVariantList preScreenCfg)
{
    mPreScreenCfg = preScreenCfg;
}

QVariantList ukccSessionServer::getPreScreenCfg()
{
    if (!mPreScreenCfg.count()) {
        QFile file(mFilePath);
        if (file.exists()) {
            file.open(QIODevice::ReadOnly);
            QByteArray readBy=file.readAll();
            QJsonParseError error;
            QJsonDocument readDoc=QJsonDocument::fromJson(readBy,&error);
            QVariantList obj = readDoc.array().toVariantList();

            Q_FOREACH(QVariant variant, obj) {
                QMap<QString, QVariant> map = variant.toMap();
                ScreenConfig screenCfg;
                screenCfg.screenId = map["id"].toString();
                screenCfg.screenModeId = map["modeid"].toString();
                screenCfg.screenPosX = map["x"].toInt();
                screenCfg.screenPosY = map["y"].toInt();
                screenCfg.isPrimary = map["primary"].toBool();
                QVariant screenVar = QVariant::fromValue(screenCfg);
                mPreScreenCfg << screenVar;
            }
        }
    }
    return mPreScreenCfg;
}

void ukccSessionServer::exitService() {
    qApp->exit();
}

void ukccSessionServer::ReloadSecurityConfig()
{
    Q_EMIT configChanged();
}

QVariantMap ukccSessionServer::getModuleHideStatus() {
    QString name = qgetenv("USER");
    if (name.isEmpty()) {
        name = qgetenv("USERNAME");
    }
    QString filename = GetSecurityConfigPath();

    return getJsonInfo(filename);
}

QString ukccSessionServer::GetSecurityConfigPath() {
    QString name = qgetenv("USER");
    if (name.isEmpty()) {
        name = qgetenv("USERNAME");
    }
    QString systemFilename = "/usr/share/ukui-control-center/data/ukui-control-center-security-config.json";
    QFile file(systemFilename);
    if (file.exists()) {
        return systemFilename;
    }

    QString userFilename = QDir::homePath() + "/.config/ukui-control-center-security-config.json";

    return userFilename;
}

void ukccSessionServer::monitoFileChanged()
{
    QFileSystemWatcher *m_FileWatcher = new QFileSystemWatcher(this);
    m_FileWatcher->addPath(GetSecurityConfigPath());
    connect(m_FileWatcher, &QFileSystemWatcher::fileChanged, this, [=](){
        Q_EMIT configChanged();
    });
}

QVariantMap ukccSessionServer::getSearchItems()
{
    m_lang = QLocale::system().name();

    // 加载插件
    LoadPlugin();
//    m_xmlFilePath.insert(QString("/usr/share/ukui-control-center/shell/res/i18n/%1.ts").arg(m_lang));

    //添加一项空数据，为了防止使用setText输入错误数据时直接跳转到list中正确的第一个页面
    clearSearchData();
    QJsonArray searchItems;
    QJsonObject rootobj;

    for (const QString i : m_xmlFilePath) {

        QString xmlPath = i.arg(m_lang);
        QFile file(xmlPath);

        if (!file.exists()) {
            qWarning() << " [SearchWidget] File not exist";
            continue;
        }

        if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
            qWarning() << " [SearchWidget] File open failed";
            continue;
        }

        QXmlStreamReader xmlRead(&file);
        QXmlStreamReader::TokenType type = QXmlStreamReader::Invalid;

        //遍历XML文件,读取每一行的xml数据都会
        //先进入StartElement读取出<>中的内容;
        //再进入Characters读取出中间数据部分;
        //最后进入时进入EndElement读取出</>中的内容
        while (!xmlRead.atEnd()) {
            type = xmlRead.readNext();
            switch (type) {
            case QXmlStreamReader::StartElement:
                m_xmlExplain = xmlRead.name().toString();
                break;
            case QXmlStreamReader::Characters:
                if (!xmlRead.isWhitespace()) {
                    if (m_xmlExplain == XML_Source) { // get xml source date
                        m_searchBoxStruct.translateContent = xmlRead.text().toString();
                    } else if (m_xmlExplain == XML_Title) {
                        if (xmlRead.text().toString() != "") // translation not nullptr can set it
                            m_searchBoxStruct.translateContent = xmlRead.text().toString();
                    } else if (m_xmlExplain == XML_Numerusform) {
                        if (xmlRead.text().toString() != "") // translation not nullptr can set it
                            m_searchBoxStruct.translateContent = xmlRead.text().toString();
                    } else if (m_xmlExplain == XML_Explain_Path) {
                        m_searchBoxStruct.fullPagePath = xmlRead.text().toString();

                        // openkylin 过滤掉的搜索项
                        if (isOpenkylin() && openkylinFilterPathList.contains(m_searchBoxStruct.fullPagePath)) {
                            continue;
                        }

                        // 非mavis搜索词过滤
                        if (filterPathList.contains(m_searchBoxStruct.fullPagePath)) {
                            continue;
                        }

                        // follow path module name to get actual module name  ->  Left module dispaly can support
                        // mulLanguages
                        m_searchBoxStruct.actualModuleName =
                                getModulesName(m_searchBoxStruct.fullPagePath.section('/', 1, 1));

                        // 插件不存在，跳过该插件的搜索项
                        if ( m_searchBoxStruct.actualModuleName.isEmpty())
                            continue;

                        if ((!g_file_test("/usr/sbin/ksc-defender", G_FILE_TEST_EXISTS) && m_searchBoxStruct.fullPagePath.contains("securitycenter",Qt::CaseInsensitive))
                                || (!isCommunity() && m_searchBoxStruct.fullPagePath.contains("update")) ){
                            break;
                        }
#ifndef __sw_64__
                        if(m_searchBoxStruct.fullPagePath.contains("Change valid",Qt::CaseInsensitive)) {
                            break;
                        }
#endif
                        QJsonObject bodyJson;
                        bodyJson.insert("plugin", m_searchBoxStruct.fullPagePath.section('/', 1, 1));
                        bodyJson.insert("plugin" + m_lang, m_searchBoxStruct.actualModuleName);
                        bodyJson.insert("subEnglish", m_searchBoxStruct.fullPagePath.section('/', 2, 2));
                        bodyJson.insert("sub" + m_lang, m_searchBoxStruct.translateContent);
                        bodyJson.insert("superordinate", getModuleType(m_searchBoxStruct.fullPagePath.section('/', 1, 1)));
                        searchItems.append(bodyJson);
                        clearSearchData();
                    }
                } else {
                    // qDebug() << "  QXmlStreamReader::Characters with whitespaces.";
                }
                break;
            case QXmlStreamReader::EndElement:
#if DEBUG_XML_SWITCH
                qDebug() << " [SearchWidget] -::EndElement: " << xmlRead.name();
#endif
                break;
            default:
                break;
            }
        }
        m_xmlExplain = "";
        clearSearchData();
        file.close();
    }
    rootobj.insert("ukcc", searchItems);
    QJsonDocument document;
    document.setObject(rootobj);
    QByteArray byteArray = document.toJson(QJsonDocument::Indented);
    QString strJson(byteArray);

    QVariantMap searchMap = rootobj.toVariantMap();

    return searchMap;
}

void ukccSessionServer::clearSearchData()
{
    m_searchBoxStruct.translateContent = "";
    m_searchBoxStruct.actualModuleName = "";
    m_searchBoxStruct.childPageName = "";
    m_searchBoxStruct.fullPagePath = "";
}

void ukccSessionServer::LoadPlugin()
{
    QDir pluginsDir = QDir(PLUGIN_INSTALL_DIRS);
    qDebug() << pluginsDir;
    foreach (QString fileName, pluginsDir.entryList(QDir::Files)) {
        determinePlugin(fileName, pluginsDir);
    }
    QDir updatePluginDir = QDir(PLUGIN_INSTALL_UPDATE);
    foreach (QString fileName, updatePluginDir.entryList(QDir::Files)) {

        determinePlugin(fileName, updatePluginDir);
    }
}

void ukccSessionServer::determinePlugin(const QString &fileName, const QDir &dir)
{
    QString pluginPath = dir.absoluteFilePath(fileName);
    qInfo() << "loading" << fileName;
    if (!fileName.endsWith(".so")) {
        return;
    }

    QPluginLoader loader(pluginPath);
    QObject * plugin = loader.instance();
    if (plugin) {
        CommonInterface * pluginInstance = qobject_cast<CommonInterface *>(plugin);
        // 插件是否启用
        if (!pluginInstance || !pluginInstance->isEnable() || !fileName.endsWith("so")) {
            return;
        }
        addModulesName(pluginInstance->name(), pluginInstance->plugini18nName(), pluginInstance->translationPath(), pluginInstance->pluginTypes());
    } else {
        //如果加载错误且文件后缀为so，输出错误
        if (fileName.endsWith(".so"))
            qDebug() << fileName << "Load Failed: " << loader.errorString() << "\n";
    }
}

bool ukccSessionServer::isOpenkylin()
{
    QString systemName = QString(QLatin1String(kdk_system_get_systemName()));
    if (systemName.compare("openkylin", Qt::CaseInsensitive) == 0) {
        return true;
    }
    return false;
}

bool ukccSessionServer::isCommunity()
{
    QString filename = "/etc/os-release";
    QSettings osSettings(filename, QSettings::IniFormat);

    QString versionID = osSettings.value("VERSION_ID").toString();

    if (versionID.compare("22.04", Qt::CaseSensitive)) {
        return false;
    }
    return true;
}

void ukccSessionServer::addModulesName(QString moduleName, QString searchName, QString translation, int type) {
    QPair<QString, QString> data;
    data.first = moduleName;
    data.second = searchName;
    QPair<QPair<QString,QString>, int> pluginNameAndtype;
    pluginNameAndtype.first = data;
    pluginNameAndtype.second = type;
    m_moduleNameList.append(pluginNameAndtype);

    if (!translation.isEmpty()) {
        m_xmlFilePath.insert(translation);
    }
}

QString ukccSessionServer::getModulesName(QString name, bool state)
{
    QString strResult = "";

    for (auto it : m_moduleNameList) {
        if (state) { //true : follow first search second (use pathName translate to actual moduleName)
            if (it.first.first == name) {
                strResult = it.first.second;
                break;
            }
        } else { //false : follow second search first (use actual moduleName translate to pathName)
            if (it.first.second == name) {
                strResult = it.first.first;
                break;
            }
        }
    }

    return strResult;
}

QString ukccSessionServer::getModuleType(QString name)
{
    int moduleType = TOTALMODULES;

    for (auto it : m_moduleNameList) {
        if (it.first.first == name) {
            moduleType = it.second;
            break;
        }
    }
    QString pluginType = "";
    switch (moduleType) {
    case SYSTEM:
        pluginType = "system";
        break;
    case ACCOUNT:
        pluginType = "account";
        break;
    case DEVICES:
        pluginType = "devices";
        break;
    case NETWORK:
        pluginType = "network";
        break;
    case PERSONALIZED:
        pluginType = "personalized";
        break;
    case DATETIME:
        pluginType = "datetime";
        break;
    case UPDATE:
        pluginType = "update";
        break;
    case SECURITY:
        pluginType = "security";
        break;
    case APPLICATION:
        pluginType = "application";
        break;
    case SEARCH_F:
        pluginType = "search";
        break;
    case TOTALMODULES:
        pluginType = "Specified";
        break;
    default:
        break;
    }
    return pluginType;
}

